package net.keebook.webapp.bean;

import java.io.IOException;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;

import net.keebook.webapp.dao.SettingDao;
import net.keebook.webapp.model.SessionModel;
import net.keebook.webapp.util.SettingId;
import net.keebook.webapp.util.oauth.OpenIdCredentials;

import org.apache.log4j.Logger;
import org.openid4java.association.AssociationException;
import org.openid4java.consumer.ConsumerException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryException;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.AuthSuccess;
import org.openid4java.message.MessageException;
import org.openid4java.message.ParameterList;
import org.openid4java.message.ax.AxMessage;
import org.openid4java.message.ax.FetchRequest;
import org.openid4java.message.ax.FetchResponse;

@Singleton
public class OpenIdBean {
	@Inject
	private SettingDao settingDao;
	private ConsumerManager consumerManager;
	
	public class Endpoint {
		public static final String GOOGLE = "https://www.google.com/accounts/o8/id";
	}

	@PostConstruct
	protected void init() {
		consumerManager = new ConsumerManager();
	}
	
	public ConsumerManager getConsumerManager() {
		return consumerManager;
	}
	
	private FetchRequest getFetchRequest(String endpointUrl) throws MessageException {
		FetchRequest fetch = null;
		if (endpointUrl.startsWith(Endpoint.GOOGLE)) {
			fetch = FetchRequest.createFetchRequest();
			//fetch.addAttribute("email", "http://axschema.org/contact/email", true); 
	        fetch.addAttribute("firstName", "http://axschema.org/namePerson/first", true); 
	        fetch.addAttribute("lastName", "http://axschema.org/namePerson/last", true);
	        fetch.addAttribute("id", "http://openid.net/schema/person/guid", true);
		}
        return fetch;
	}
	
	public void login(SessionModel sessionModel, FacesContext context, String endpointUrl) throws IOException {
		String root = getSettingDao().get(SettingId.PUBLIC_URL).getValue();
		if (!root.endsWith("/")) {
			root += "/";
		}
		String returnUrl = root + "services/openid";
		
		ConsumerManager manager = getConsumerManager();
		try {
			List<?> discoveries = manager.discover(endpointUrl);
			DiscoveryInformation discovered = manager.associate(discoveries);
			sessionModel.setDiscoveryInformation(discovered);
			AuthRequest authReq = manager.authenticate(discovered, returnUrl);
			FetchRequest fetch = getFetchRequest(endpointUrl);
			if (fetch != null) {
				authReq.addExtension(fetch);
			}
			context.getExternalContext().redirect(authReq.getDestinationUrl(true));
		} catch (DiscoveryException e) {
			e.printStackTrace();
			Logger.getLogger(this.getClass()).error("OpenID discovery failed", e);
		} catch (ConsumerException e) {
			e.printStackTrace();
			Logger.getLogger(this.getClass()).error("OpenID auch request failed", e);
		} catch (MessageException e) {
			e.printStackTrace();
			Logger.getLogger(this.getClass()).error("OpenID auch request failed", e);
		}
	}
	
	public OpenIdCredentials verify(SessionModel sessionModel, HttpServletRequest request) {
		ConsumerManager consumerManager = getConsumerManager();
		ParameterList openidResp = new ParameterList(request.getParameterMap());
		DiscoveryInformation discovered = sessionModel.getDiscoveryInformation();
		StringBuffer receivingURL = request.getRequestURL();
		String queryString = request.getQueryString();
		if (queryString != null && queryString.length() > 0) {
			receivingURL.append("?").append(request.getQueryString());
		}
		try {
			VerificationResult verification = consumerManager.verify(receivingURL.toString(), openidResp, discovered);
			Identifier verified = verification.getVerifiedId();
			if (verified != null) {
				OpenIdCredentials credentials = new OpenIdCredentials(verified.getIdentifier());
				AuthSuccess authSuccess = (AuthSuccess)verification.getAuthResponse();
				if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
					FetchResponse fetchResponse = (FetchResponse)authSuccess.getExtension(AxMessage.OPENID_NS_AX);
					credentials.setName(fetchResponse.getAttributeValue("firstName"));
					System.out.println("guid: " + fetchResponse.getAttributeValue("id"));
					//credentials.setEmail(fetchResponse.getAttributeValue("email"));
				}
				return credentials;
			}
			return null;
		} catch (AssociationException e) {
			Logger.getLogger(this.getClass()).error("OpenID verification failed", e);
		} catch (DiscoveryException e) {
			Logger.getLogger(this.getClass()).error("OpenID discovery failed", e);
		} catch (MessageException e) {
			Logger.getLogger(this.getClass()).error("OpenID verification failed", e);
		}
		return null;
	}

	public SettingDao getSettingDao() {
		return settingDao;
	}

	public void setSettingDao(SettingDao settingDao) {
		this.settingDao = settingDao;
	}
}
